import logging
from functools import lru_cache
from math import degrees

from .core import *

pygame.display.init()


class SquareBody(SquareSprite):
    """__init__(self, master, subdevices = [])"""

    def __init__(self, master, square: Square, **kwargs):
        """kwargs:
        subdevice: a instance of Subdevice
        xmlpath: a path for anima"""
        # 继承 and 关联
        if kwargs.get("xmlpath") is not None:
            super().__init__(square, xmlpath = kwargs.get("xmlpath"))
        else:
            super().__init__(square)

        # father and child
        self.master = master
        if master != None:
            master.add(self)
        self.subdevice = kwargs.get("subdevices")


        self.mass = kwargs.get("mass", 100)
        self._velocity = Vector2()
        self.kf = 1
        self.e = 0.4

        # {name : [force, time]}
        self.forces: dict = {}

        # DEBUG
        self.debug = kwargs.get("debug", False)

    @property
    def velocity(self):
        return self._velocity

    @velocity.setter
    def velocity(self, val):

        self._velocity = val

        # if self._velocity.length() < 1:
        #     self._velocity = Vector2(0)


    def set_master(self, master: pygame.sprite.Group):
        self.master = master
        master.add(self)

    def given_impulse(self, impulse: Vector2, pos = None):
        if pos is  None:
            pos = self.square.center
        self.master.given_impulse(impulse, pos)

    def add_force(self, name, force: Vector2, time: int):
        self.forces[name] = [force, time]

    def seperate(self, delta_mass, launch_vec):
        """seprtate（分离质量，发射速度）"""
        if self.mass - delta_mass > 0:
            self.given_impulse(-delta_mass * launch_vec)
            self.mass -= delta_mass
            return True
        else:
            logging.warning("mass cannot be less than 0")
            return False

    def _physics_process(self, delta):
        pass

    def physics_process(self, delta: int):

        self._physics_process(delta)

        for name, f_and_t in list(self.forces.items()):

            if f_and_t[1] != -1:  # 如果不是恒力
                f_and_t[1] -= delta

                # 判断力作用是否结束
                if f_and_t[1] <= 0:
                    del self.forces[name]

            self.given_impulse(f_and_t[0] * delta) 

        # 摩擦力
        if delta * self.kf * self.velocity.length() < self.mass * self.velocity.length():
            self.given_impulse((-self.kf) * self.velocity * delta)
        else:
            self.velocity = Vector2()

        

        self.square.center += self.velocity * delta

        self.rotation = self.master.rotation

    def _process(self, delta):
        pass

    def process(self, delta: int):
        self._process(delta)

        self.anima.update(delta)
        Camera.blit(self.anima.image, self.rect.topleft)

        if self.debug:
            pygame.draw.circle(Camera.screen, "red", Camera.campos(self.square.center), 2)



class RigidSquarePS(pygame.sprite.Group):
    '''rotation is forced to be the same as RSPS'''

    def __init__(self, **kwargs):
        super().__init__()


        self.omega = Vector3()
        self.rotation = 0.0
        self.velocity = Vector2()

        self.debug = kwargs.get("debug", False)

    @property
    def mass(self):
        m = 0
        for mp in self.sprites():
            m += mp.mass
        return m


    @property
    def mc(self):
        temp = Vector2()

        for mp in self.sprites():
            temp += mp.mass * mp.square.center

        return temp / self.mass

    @property
    def J(self):
        j = 0

        for mp in self.sprites():
            j += mp.mass * (mp.square.center - self.mc).magnitude_squared()

        return j

    def given_impulse(self, impulse: Vector2, pos: Vector2|None = None):
        if pos is None:
            pos = self.mc

        self.velocity += impulse / self.mass
        j = self.J
        if j != 0:
            self.omega.z += (pos - self.mc).cross(impulse) / j

    def physics_process(self, delta):
        # badly :         self.rotation += self.omega.z * delta
        # I directly add an '-'
        self.rotation += -self.omega.z * delta
        for mp in self.sprites():
            mp.velocity = self.velocity + toVector2(
                self.omega.cross(toVector3(mp.square.center - self.mc)))

        for mp in self.sprites():
            mp.physics_process(delta)

    def process(self, delta):
        for mp in self.sprites():
            mp.process(delta)

        if self.debug:
            # 画各质点
            for mp in self.sprites():
                mp.square.draw()

            # 画质心
            pygame.draw.circle(Camera.screen, "green", Camera.campos(self.mc), 5)

    def update(self, delta):
        self.physics_process(delta)
        self.process(delta)
